#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libspe2.h>
#include <cbe_mfc.h>
#include <pthread.h>
#include <unistd.h>
#include <math.h>
#include "common.h"

//============================================================================
// This part is input
int arrayInPPE[ARRAY_SIZE]  QWORD_ALIGNED;
//============================================================================
CONTROL_BLOCK cb QWORD_ALIGNED;
extern spe_program_handle_t spu;

// Data structure for running SPE thread
typedef struct ppu_pthread_data {
  spe_context_ptr_t speid;
  pthread_t pthread;
  void *argp;
} ppu_pthread_data_t;

ppu_pthread_data_t data;

// create and run one SPE thread
void *ppu_pthread_function(void *arg) {
  ppu_pthread_data_t *datap = (ppu_pthread_data_t *)arg;
  int rc;
  unsigned int entry = SPE_DEFAULT_ENTRY;
  if ((rc = spe_context_run(datap->speid, &entry, 0, datap->argp, NULL, NULL)) < 0) {
    fprintf (stderr, "Failed spe_context_run(rc=%d, errno=%d, strerror=%s)\n", rc, errno, strerror(errno));
    exit (1);
  }
  pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
  
  int rc;
  int i;

  //============================================================================  
  // Initialize array elements
  for(i=0; i<ARRAY_SIZE; i++)
	  arrayInPPE[i] = i+1;

  // Fill in control block
  cb.data = (unsigned int)arrayInPPE;
  //============================================================================
  
  // create SPE context and load SPE program into the SPE context
  if ((data.speid = spe_context_create (0, NULL)) == NULL)
    {
      fprintf (stderr, "Failed spe_context_create(errno=%d strerror=%s)\n", errno, strerror(errno));
      exit (3);
    }
  
  //Load program
  if ((rc = spe_program_load (data.speid, &spu)) != 0)
    {
      fprintf (stderr, "Failed spe_program_load(errno=%d strerror=%s)\n", errno, strerror(errno));
      exit (3);
    }
  
  //============================================================================
  //This part is used to Data arrayInPPE for spu.c in only one SPE
  
  data.argp = (unsigned long long *) &cb;

#ifdef TIMING  
  /********************************************************/
  long unsigned int start_t, end_t;
  start_t=__mftb();
  /********************************************************/
#endif

  //============================================================================
  
  // create SPE pthreads
  if ((rc = pthread_create (&data.pthread, NULL, &ppu_pthread_function, &data)) != 0)
    {
      fprintf (stderr, "Failed pthread_create(errno=%d strerror=%s)\n", errno, strerror(errno));
      exit (3);
    }      
  
  // wait for SPE to complete
  if ((rc = pthread_join (data.pthread, NULL)) != 0)
    {
      fprintf (stderr, "Failed pthread_join(rc=%d, errno=%d strerror=%s)\n", rc, errno, strerror(errno));
      exit (1);
    }

#ifdef TIMING  
  //compute the timein us(10E-6)       
  end_t=__mftb();
  printf("\n\n\nTotal time used by SPE: %f us\n\n\n", (float)((end_t-start_t)/79.8)); 
#endif
  
  // destroy the SPE contexts	
  if ((rc = spe_context_destroy (data.speid)) != 0)
    {
      fprintf (stderr, "Failed spe_context_destroy(rc=%d, errno=%d strerror=%s)\n", rc, errno, strerror(errno));
      exit (1);
    }

   /* Print out the new value */
  for(i=0; i<ARRAY_SIZE; i++)
	  printf("%d ",arrayInPPE[i]);
  
  return 0;
}



